package com.stark.commons.lang.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.extractor.POITextExtractor;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.PicturesManager;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.hwpf.usermodel.Picture;
import org.apache.poi.hwpf.usermodel.PictureType;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.w3c.dom.Document;

import lombok.Cleanup;

/**
 * POI 处理 WORD 方法集合。
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
public class WordUtil {
	
	/**
	 * Word 97-2003 (.doc)
	 */
	public static final int DOC = 0;
	/**
	 * Word (.docx)
	 */
	public static final int DOCX = 1;
	
	private static final String BAD_FILE_FORMAT_EXCEPTION = "Word文件格式不正确";
	
	/**
	 * 识别文件类型。
	 * @param bytes word 字节码。
	 * @return {@link #DOC} 或 {@link #DOCX} 。
	 */
	public static int detectType(byte[] bytes) {
		try {
			getText(bytes, WordUtil.DOC);
			return WordUtil.DOC;
		} catch (Exception e1) {
			try {
				getText(bytes, WordUtil.DOCX);
				return WordUtil.DOCX;
			} catch (Exception e2) {
				throw new IllegalArgumentException(BAD_FILE_FORMAT_EXCEPTION);
			}
		}
	}
	
	/**
	 * 获取文件后缀，不包含 "." 。
	 * @param bytes word 字节码。
	 * @return 文件后缀，不包含 "." 。
	 */
	public static String getSuffix(byte[] bytes) {
		int type = detectType(bytes);
		return type == DOCX ? "docx" : "doc";
	}
	
	/**
	 * 从 word 中读取文本。
	 * <p>支持 DOC 和 DOCX 。
	 * @param in 输入流对象.
	 * @param fileType {@link WordUtil#DOC} 或 {@link WordUtil#DOCX}
	 * @return word 中的文本。
	 * @throws IOException
	 */
	public static String getText(InputStream in, int fileType) throws IOException {
		@Cleanup POITextExtractor extractor = null;
		if (fileType == DOC) {
			extractor = new WordExtractor(in);
		} else if (fileType == DOCX) {
			@Cleanup XWPFDocument docx = new XWPFDocument(in); 
			extractor = new XWPFWordExtractor(docx);
		} else {
			throw new IOException(BAD_FILE_FORMAT_EXCEPTION);
		}
		String text = extractor.getText();
		return text;
	}
	
	/**
	 * 从 word 中读取文本。
	 * @param wordPath word 文件路径。
	 * @return word 中的文本。
	 * @throws IOException
	 * @see #getText(InputStream, int)
	 */
	public static String getText(String wordPath) throws IOException {
		@Cleanup InputStream in = new FileInputStream(new File(wordPath));
		String text = getText(in, getDocType(wordPath));
		return text;
	}
	
	/**
	 * 从 word 中读取文本。
	 * @param bytes word 字节码。
	 * @param fileType {@link WordUtil#DOC} 或 {@link WordUtil#DOCX}
	 * @return word 中的文本。
	 * @throws IOException
	 * @see #getText(byte[], int)
	 */
	public static String getText(byte[] bytes, int fileType) throws IOException {
		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		String text = getText(in, fileType);
		return text;
	}
	
	/**
	 * word 转 html 。
	 * <p>暂时仅支持 DOC ；生成的 html 通过 destDir + "/index.html" 访问。
	 * @param in 输入流对象。
	 * @param fileType {@link WordUtil#DOC} 或 {@link WordUtil#DOCX}
	 * @param destDir html 文件存储目录。
	 * @param encoding 字符编码。
	 * @throws Exception
	 * @see #doc2Html(InputStream, String, String)
	 */
	public static void word2Html(InputStream in, int fileType, String destDir, String encoding)
			throws Exception {
		if (fileType == DOC) {
			doc2Html(in, destDir, encoding);
		} else if (fileType == DOCX) {
			// docx2Html(in, destDir, encoding);
		}
	}
	
	/**
	 * word 转 html 。
	 * <p>暂时仅支持 DOC ；生成的 html 通过 destDir + "/index.html" 访问。
	 * @param wordPath word 文件路径。
	 * @param destDir html 文件存储目录。
	 * @param encoding 字符编码。
	 * @throws Exception
	 * @see #word2Html(InputStream, int, String, String)
	 */
	public static void word2Html(String wordPath, String destDir, String encoding) throws Exception {
		@Cleanup InputStream in = new FileInputStream(wordPath);
		word2Html(in, getDocType(wordPath), destDir, encoding);
	}
	
	/**
	 * word 转 html 。
	 * <p>支持 DOC 和 DOCX ；生成的 html 通过 destDir + "/index.html" 访问。
	 * @param bytes word 字节码
	 * @param fileType {@link WordUtil#DOC} 或 {@link WordUtil#DOCX}
	 * @param destDir html 文件存储目录
	 * @param encoding 字符编码
	 * @throws Exception
	 * @see #word2Html(InputStream, int, String, String)
	 */
	public static void word2Html(byte[] bytes, int fileType, String destDir, String encoding)
			throws Exception {
		InputStream is = new ByteArrayInputStream(bytes);
		word2Html(is, fileType, destDir, encoding);
	}
	
	/**
	 * 根据文件名获取 WORD 文件类型。
	 * @param filename 文件名
	 * @return {@link WordUtil#DOC} 或 {@link WordUtil#DOCX}
	 */
	public static int getDocType(String filename) {
		if (StringUtils.isBlank(filename)) {
			return -1;
		}
		
		filename = filename.trim().toLowerCase();
		if (!filename.contains(".")) {
			return -1;
		}
		
		String suffix = StringUtils.substringAfterLast(filename, ".");
		if ("doc".equalsIgnoreCase(suffix)) {
			return DOC;
		}
		if ("docx".equalsIgnoreCase(suffix)) {
			return DOCX;
		}
		return -1;
	}
	
	/**
	 * DOC 文档转 html 。
	 * @param in 输入流对象。
	 * @param destDir html 文件存储目录。
	 * @param encoding 字符编码。
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws TransformerException
	 */
	private static void doc2Html(InputStream in, String destDir, String encoding)
			throws ParserConfigurationException, TransformerException, IOException {
		destDir += File.separator;
		FileUtils.createDirectory(destDir);
		
		@Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream();
		HWPFDocument wordDocument = new HWPFDocument(in);
		WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
				DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
		wordToHtmlConverter.setPicturesManager(new PicturesManager() {
			public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches,
					float heightInches) {
				return suggestedName;
			}
		});
		wordToHtmlConverter.processDocument(wordDocument);
		List<Picture> pics = wordDocument.getPicturesTable().getAllPictures();
		if (pics != null) {
			Picture pic;
			for (int i = 0; i < pics.size(); i++) {
				pic = pics.get(i);
				pic.writeImageContent(new FileOutputStream(destDir + pic.suggestFullFileName()));
			}
		}
		Document htmlDocument = wordToHtmlConverter.getDocument();
		DOMSource domSource = new DOMSource(htmlDocument);
		StreamResult streamResult = new StreamResult(out);
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer serializer = tf.newTransformer();
		serializer.setOutputProperty(OutputKeys.ENCODING, "GB2312");
		serializer.setOutputProperty(OutputKeys.INDENT, "yes");
		serializer.setOutputProperty(OutputKeys.METHOD, "html");
		serializer.transform(domSource, streamResult);
		String content = out.toString("GB2312")
				.replace("charset=GB2312", "charset=" + encoding);
		String htmlPath = destDir + "index.html";
		FileUtils.write(new File(htmlPath), content, encoding);
	}
	
	/**
	 * DOCX 文档转 html 。
	 * @param in 输入流对象。
	 * @param fileType {@link WordUtil#DOC} 或 {@link WordUtil#DOCX}
	 * @param destDir html 文件存储目录。
	 * @param encoding 字符编码。
	 * @throws IOException 
	 * @throws XWPFConverterException 
	 */
	/*private static void docx2Html(InputStream in, String destDir, String encoding)
			throws IOException, XWPFConverterException {
		destDir += File.separator;
		FileUtil.createDirectory(destDir);
		
        File imageFolder = new File(destDir);
        XHTMLOptions options = XHTMLOptions.create();
        options.URIResolver(new FileURIResolver(imageFolder));
        options.setExtractor(new FileImageExtractor(imageFolder));
        // options.setIgnoreStylesIfUnused(true);
        // options.setFragment(true);
        
        FileUtil.createFile(destDir + "index.html");
        OutputStream out = new FileOutputStream(destDir + "index.html");
        XWPFDocument docx = new XWPFDocument(in);
        XHTMLConverter.getInstance().convert(docx, out, options);
	}*/
	
}
